/**
 * 
 */
package it.unical.canonaco.rende.igpe.game.cbv.logiclayer.engine.component.pawn;

import java.util.ArrayList;
import java.util.HashMap;

import it.unical.canonaco.rende.igpe.game.cbv.logiclayer.engine.component.pawn.stats.Role;
import it.unical.canonaco.rende.igpe.game.cbv.logiclayer.engine.component.pawn.stats.Stats;
import it.unical.canonaco.rende.igpe.game.cbv.logiclayer.engine.component.player.SimplePlayer;

/**
 * @author Massimo Canonaco
 */
public class Character implements GenericPawn {

	/**Checks if the point (x2,y2) is inside the circle with that radius and center in (x1,y1).
	 * @param x1 			It's the row value of the center of the circle.
	 * @param x2			It's the row value of the point.
	 * @param y1			It's the column value of the center of the circle.
	 * @param y2			It's the column value of the point.
	 * @return True, the point is inside. False, false the point isn't inside. */
	static public boolean isInRange(final int x1, final int x2, final int y1, final int y2, final float radius)
	{
		int xD = x1 - x2, yD = y1 - y2;
		return (xD*xD) + (yD*yD) <= radius*radius;
	}

	/**Return the level for that experience.
	 * @param experience 		is the amount of experience for calculating the level.
	 * @return The right level for that experience.*/
	static public int whatIsLevel(final int experience)
	{
		return (int) Math.round(((Character.LEVELLING_FACTOR *  Math.sqrt(experience))/EXPERIENCE_FACTOR));
	}
	
	/**Return the minimum amount of experience for reaching the level.
	 * @param level 			is the level.
	 * @return The amount of experience. */
	static public int howMuchExperience(final int level)
	{
		return Math.round(	((level*level)*(EXPERIENCE_FACTOR*EXPERIENCE_FACTOR))
				/(Character.LEVELLING_FACTOR * Character.LEVELLING_FACTOR));
	}
	
	/**It's the the constant which is used for determining the experience to be reached for the level. */
	static private final int LEVELLING_FACTOR = 2;
	
	/**It's a constant which is used in the calculation of leveling and experience. */
	static private final int EXPERIENCE_FACTOR = 57;
	
//	/**It's a constant which is used for saying character isn't on chessboard. */
//	static private final int NO_POSITION = -1;
	
	/**Row value of the character position into the arena. */
	private int rowPosition;
	
	/**Column value of the character position into the arena. */
	private int columnPosition;
	
	/**It signals if the character has been moved. It supports rowPosition and columnPosition. In fact GameManager can use this value for checking if character is moved or not.*/
	private boolean moved; 
	
	/**It means the traveled distance during his movements. This variable helps the GameManager to restore quickly the original movement range. */
	private float traveled;
	
	/** It's the name of the character */
	private String name;
	
	/**It contains the current statistics of the character, which are based on the belonging race.
	 * They are used in-game, so they can be altered
	 * */
	private Role stats;
	
	/**It's used in combats and it expresses if the character is alive or dead. </br>
	 * <b>true</b> : alive </br>
	 * <b>false</b>: dead.*/
	private boolean alive;
	
	/**It's the current level of the character. */
	private int level;
	
	/**It's the current experience of the character. */
	private int experience;
	
	/**Player who owns this character. */
	private SimplePlayer owner;
	
	private Inventory inventory;
	
	/** It indicates wheter this character has already casted a spell. 
	 *  It's used to limit casting spells once per turn for each character. 
	 **/
	private boolean spellCastable;
	
	/** */
	public Character(final int lvl, final int exp, final SimplePlayer possessor)
	{
		level = lvl;
		owner = possessor;
		experience = exp;
		traveled = 0;
		rowPosition = columnPosition = NO_POSITION;
		spellCastable = true;
		inventory = new Inventory();
	}
	
	/* (non-Javadoc)
	 * @see it.unical.canocaco.rende.igpe.game.cbv.logiclayer.engine.component.pawn.GenericPawn#setPosition(int, int)
	 */
	@Override
	public void setPosition(int row, int column) {
		
		// Update the new position values
		rowPosition = row;
		columnPosition = column;
		
		// Mark him as "moved".
		moved = true;
	}

	/** */
	public float getProgressExperience()
	{
		return ((float)experience/Character.howMuchExperience(level+1))*100;
	}
	/* (non-Javadoc)
	 * @see it.unical.canocaco.rende.igpe.game.cbv.logiclayer.engine.component.pawn.GenericPawn#getRow()
	 */
	@Override
	public int getRow() {
		return rowPosition;
	}

	/* (non-Javadoc)
	 * @see it.unical.canocaco.rende.igpe.game.cbv.logiclayer.engine.component.pawn.GenericPawn#getColumn()
	 */
	@Override
	public int getColumn() {
		return columnPosition;
	}

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * @return the statistics
	 */
	public Role getStats() {
		return stats;
	}

	/**
	 * @param stats the statistics to set
	 */
	public void setStats(Role stats) 
	{
		this.stats = stats;
	}

	/**
	 * @return the alive
	 */
	public boolean isAlive() {
		if(alive && stats.getCharacteristics().getHealth() <= 0)
		{
			alive = false;
		}
		return alive;
	}

	/**
	 * @param alive the alive to set
	 */
	public void setAlive(boolean alive) {
		this.alive = alive;
	}

	/**
	 * @return the level
	 */
	public int getLevel() {
		return level;
	}

	/**
	 * @param level the level to set
	 */
	public void setLevel(int level) {
		this.level = level;
	}

	/**
	 * @return the experience
	 */
	public int getExperience() {
		return experience;
	}

	/**Set the experience and eventually update the level and the experience.
	 * @param experience the experience to set. */
	public void setExperience(int experience) {
		// Set the new experience.
		this.experience = experience;
		int tempExperience = -1;
		
		// If the level isn't right for this new experience.
		while(level < Character.whatIsLevel(experience))
		{
			// Get the next level.
			++level;
			
			// Calculate the experience for the next level.
			tempExperience = Character.howMuchExperience(level);
		
			if(experience > tempExperience)
			{
				experience -= tempExperience;	
			}
		}
	}

	/**
	 * @return the owner
	 */
	public SimplePlayer getOwner() {
		return owner;
	}

	/**
	 * @param owner the owner to set
	 */
	public void setOwner(SimplePlayer owner) {
		this.owner = owner;
	}

	/** */
	public boolean isInMovementRange(final int xCoordinate, final int yCoordinate)
	{
		int xD = this.rowPosition - xCoordinate, yD = this.columnPosition - yCoordinate;
		float radius = stats.getCharacteristics().getMovementRange();
		return (xD*xD) + (yD*yD) <= radius*radius;
	}

	/**
	 * @return the moved
	 */
	public boolean isMoved() 
	{
		return moved;
	}
	
	/** */
	public void makeUnmoved()
	{
		moved = false;
	}

	/**
	 * @return the traveled
	 */
	public float getTraveled() 
	{
		return traveled;
	}

	/**
	 * @param traveled the traveled to set
	 */
	public void setTraveled(float traveled) 
	{
		this.traveled = traveled;
	}

	/** */
	public boolean isInAttackRange(final int xCoordinate, final int yCoordinate) 
	{
		int xD = this.rowPosition - xCoordinate, yD = this.columnPosition - yCoordinate;
		float radius = stats.getCharacteristics().getAttackRange();
		return (xD*xD) + (yD*yD) <= radius*radius;
	}
	
	/** */
	public void doLightWeight()
	{
		stats.setAbilityStats(null);
		stats.setCharacteristics(null);
	}

	public Inventory getInventory() {
		return inventory;
	}

	public void setInventory(Inventory inventory) {
		this.inventory = inventory;
	}

	@Override
	public String getType() 
	{
		if(stats != null)
			return stats.getName();
		
		return "";
	}

	public boolean isSpellCastable() {
		return spellCastable;
	}

	public void setSpellCastable(boolean spellCastable) {
		this.spellCastable = spellCastable;
	}


}
